package tk.mybatis.springboot.service;

import com.github.pagehelper.PageHelper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;
import tk.mybatis.springboot.mapper.UserInfoMapper;
import tk.mybatis.springboot.model.TestThread;
import tk.mybatis.springboot.model.UserInfo;
import tk.mybatis.springboot.util.ThreadFactoryUtil;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.atomic.LongAdder;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author liuzh
 * @since 2016-01-31 21:42
 */
@Slf4j
@Service
public class UserInfoService {

    @Autowired
    private UserInfoMapper userInfoMapper;

    public List<UserInfo> getAll(UserInfo UserInfo) {
        if (UserInfo.getPage() != null && UserInfo.getRows() != null) {
            PageHelper.startPage(UserInfo.getPage(), UserInfo.getRows());
        }
        return userInfoMapper.selectAll();
    }

    public UserInfo getById(Integer id) {
        return userInfoMapper.selectByPrimaryKey(id);
    }

    public void deleteById(Integer id) {
        userInfoMapper.deleteByPrimaryKey(id);
    }

    public void save(UserInfo country) {
        if (country.getId() != null) {
            userInfoMapper.updateByPrimaryKey(country);
        } else {
            userInfoMapper.insert(country);
        }
    }

    //1000000 500M, 10000,3.39 {"SUM":"Fork/join sum: 1000000 in 25252 ms."}
    // {"SUM":"Fork/join sum: 1000000 in 25439 ms."}
    static int size = 4000000;
    /**
     * 批量插入 目前
     * 应用主机cpu；利用率 100%
     * 服务器mysql 主机cpu利用率 >100%  <200%  (机器核心2核，最大cpu %200,与核数有关)
     *  mysql 参数最大传输  max_allowed_packet= 10M
     *  服务器带宽 3M，
     *  最终调节每次 批量insert
     */

    /**
     * fork join 插入
     * Fork/join sum: 3000000 in 121389 ms.  批量insert个数：  46875
     * Fork/join sum: 2000000 in 57018 ms.  批量insert个数：  31250
     *
     * @param inputSize
     * @return
     */
    public String saveMore(Integer inputSize) {
        if (inputSize != null) {
            size = inputSize;
        }
        // 创建随机数组成的数组:
        List<UserInfo> userInfos = new ArrayList<>(size);
        fillRandom(userInfos);
        // fork/join task:
        // 最大并发数4
        ForkJoinPool fjp = new ForkJoinPool(16);
        ForkJoinTask<Integer> task = new UserInfoSaveTask(userInfos, 0, userInfos.size(), userInfoMapper);
        long startTime = System.currentTimeMillis();
        Integer result = fjp.invoke(task);
        long endTime = System.currentTimeMillis();
        System.out.println("Fork/join sum: " + result + " in " + (endTime - startTime) + " ms.");
        return "Fork/join sum: " + result + " in " + (endTime - startTime) + " ms.";
    }

    /**
     * 正常插入
     * //{"SUM":"nomal sum: 3000000 in 1139581 ms."} {"SUM":"nomal sum: 1000000 in 61630 ms."}
     *
     * @param inputSize
     * @return
     */

    public String saveNormal(Integer inputSize) {
        if (inputSize != null) {
            size = inputSize;
        }
        // 创建随机数组成的数组:
        List<UserInfo> userInfos = new ArrayList<>(size);
        fillRandom(userInfos);
        int loop = userInfos.size() / UserInfoSaveTask.THRESHOLD;
        long startTime = System.currentTimeMillis();
        int result = 0;
        for (int i = 0; i < loop; i++) {
            int start = i * UserInfoSaveTask.THRESHOLD;
            int end = (i + 1) * UserInfoSaveTask.THRESHOLD;
            if (loop - 1 == i) {
                end = userInfos.size();
            }
            int sum = userInfoMapper.insertList(userInfos.subList(start, end));
            result = sum + result;
        }
        long endTime = System.currentTimeMillis();
        System.out.println("nomal sum: " + result + " in " + (endTime - startTime) + " ms.");
        return "nomal sum: " + result + " in " + (endTime - startTime) + " ms.";
    }

    /**
     * 多线程插入
     * {"SUM":"saveMoreThread sum: 4000000 in 105063 ms."}
     * saveMoreThread sum: 3000000 in 144648 ms.
     * {"SUM":"saveMoreThread sum: 2000000 in 50902 ms."}  每次insert 50000
     * {"SUM":"saveMoreThread sum: 2000000 in 44446 ms."}  每次insert 45000
     * {"SUM":"saveMoreThread sum: 2000000 in 46123 ms."} 每次insert 45000
     * {"SUM":"saveMoreThread sum: 2000000 in 40809 ms."}  每次insert 48000
     * {"SUM":"saveMoreThread sum: 2000000 in 41063 ms."}  每次insert 48000
     *
     * @param inputSize
     * @return
     */
    public String saveMoreThread(Integer inputSize) {
        if (inputSize != null) {
            size = inputSize;
        }
        // 创建随机数组成的数组:
        List<UserInfo> userInfos = new ArrayList<>(size);
        fillRandom(userInfos);
        int loop = userInfos.size() / UserInfoSaveTask.THRESHOLD;
        long startTime = System.currentTimeMillis();
        LongAdder result = new LongAdder();
        CountDownLatch latch = new CountDownLatch(loop);
        for (int i = 0; i < loop; i++) {
            int start = i * UserInfoSaveTask.THRESHOLD;
            int end = (i + 1) * UserInfoSaveTask.THRESHOLD;
            if (loop - 1 == i) {
                end = userInfos.size();
            }

            final int finalEnd = end;
            ThreadFactoryUtil.threadPool.execute(() -> {
                try {
                    int sum = userInfoMapper.insertList(userInfos.subList(start, finalEnd));
                    result.add(sum);
                } catch (Exception e) {
                    log.error("saveMoreThread  error", e);
                } finally {
                    latch.countDown();
                }
            });

        }

        try {
            latch.await();

        } catch (InterruptedException e) {
            log.error("saveMoreThread 错误", e);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("saveMoreThread sum: " + result + " in " + (endTime - startTime) + " ms.");
        return "saveMoreThread sum: " + result + " in " + (endTime - startTime) + " ms.";
    }

    private static void fillRandom(List<UserInfo> array) {
        for (int i = 0; i < size; i++) {
            UserInfo userInfo = new UserInfo();
            userInfo.setUsername("test" + i);
            userInfo.setPassword("pas" + i);
            userInfo.setEmail("email" + i);
            userInfo.setUsertype("0");
            userInfo.setEnabled(i);
            userInfo.setQq("qq" + i);
            userInfo.setTel("tel" + i);
            array.add(userInfo);
        }
    }

    private static void fillRandom(List<UserInfo> array, int size, String key) {
        for (int i = 0; i < size; i++) {
            UserInfo userInfo = new UserInfo();
            userInfo.setUsername(key + "test" + i);
            userInfo.setPassword(key + "pas" + i);
            userInfo.setEmail("email" + i);
            userInfo.setUsertype("0");
            userInfo.setEnabled(i);
            userInfo.setQq(key + "qq" + i);
            userInfo.setTel(key + "tel" + i);
            array.add(userInfo);
        }
    }

    public void trancate() {
        userInfoMapper.trancate();
    }

    public List<UserInfo> concurrentUpdate() {
        List<UserInfo> userInfos = new ArrayList<>();
        fill(userInfos);
        userInfoMapper.insertList(userInfos);
        return userInfos;
    }

    @Transactional(rollbackFor = Exception.class)
    public void concurrentUpdateTran(List<UserInfo> userInfos) {
        for (UserInfo userInfo : userInfos) {
            userInfo.setEmail("1828581413@qq.com.test");
            log.info("id: {}", userInfo.getId());
            userInfoMapper.updateByPrimarySelective(userInfo);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void concurrentUpdateTran2(List<UserInfo> userInfos) {
        /**
         * 根据username 修改
         */
//        for (UserInfo userInfo : userInfos) {
//            userInfo.setEmail("1828581413@qq.com.test2");
//            log.info("username: {}", userInfo.getUsername());
//
//            userInfoMapper.updateByColSelective(userInfo);
//        }
        /**
         * 先查询.然后根据id修改
         */
        Example username1 = new Example(UserInfo.class);
        username1.createCriteria().andEqualTo("username", userInfos.get(0).getUsername());
        List<UserInfo> username = userInfoMapper.selectByExample(username1);
        for (UserInfo info : username) {
            info.setEmail("1828581413@qq.com.test3");
            userInfoMapper.updateByPrimarySelective(info);
        }

    }

    public void fill(List<UserInfo> array) {
        for (int i = 0; i < 200; i++) {
            UserInfo userInfo = new UserInfo();
            userInfo.setUsername("test");
            userInfo.setPassword("pas");
            userInfo.setEmail("email");
            userInfo.setUsertype("0");
            userInfo.setEnabled(i);
            userInfo.setQq("qq");
            userInfo.setTel("tel");
            array.add(userInfo);
        }
    }


    /**
     * 测试多线程事务
     *
     * @param inputSize
     * @return
     */
    public String testThread(Integer inputSize) {
        if (inputSize != null) {
            size = inputSize;
        }
        // 创建随机数组成的数组:
        List<UserInfo> userInfos = new ArrayList<>(size);
        fillRandom(userInfos);
        int loop = userInfos.size() / UserInfoSaveTask.THRESHOLD;
        long startTime = System.currentTimeMillis();
        int result = 0;
        for (int i = 0; i < loop; i++) {
            int start = i * UserInfoSaveTask.THRESHOLD;
            int end = (i + 1) * UserInfoSaveTask.THRESHOLD;
            if (loop - 1 == i) {
                end = userInfos.size();
            }
            int sum = userInfoMapper.insertList(userInfos.subList(start, end));
            result = sum + result;
        }
        long endTime = System.currentTimeMillis();
        System.out.println("testThread sum: " + result + " in " + (endTime - startTime) + " ms.");
        return "testThread sum: " + result + " in " + (endTime - startTime) + " ms.";
    }

    static ConcurrentHashMap<String, TestThread> map = new ConcurrentHashMap<>();


    public Object testThreadLock(String key) {
        TestThread lock = getLock(key);
        lock.lock();
        try {
            UserInfoService bean = TestAsyncService.applicationContext.getBean(UserInfoService.class);
            new Thread(() -> {
                bean.insert1(key, lock);
            }).start();
            new Thread(() -> {
                bean.insert2(key, lock);
            }).start();
            // 方法都成功 返回
            while (!lock.allSuccess()) {
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        } catch (Exception e) {
            log.error("执行方法1+2 错误", e);
            return e.getMessage();
        } finally {
            lock.unlock();
            removeLock(key, lock);
        }
        return "ok";
    }

    public Object testThreadLock1(String key) {
        TestThread lock = getLock(key);
        lock.lock();
        try {
            Thread.sleep(20000);
        } catch (Exception e) {
            log.error("执行方法1+2 错误", e);
            return e.getMessage();
        } finally {
            lock.unlock();
            removeLock(key, lock);
        }
        return "ok";
    }


    @Transactional(rollbackFor = Exception.class)
    public void insert1(String key, TestThread lock) {
        try {
            userInfoMapper.insert(createOneUser(key));
            Thread.sleep(2000);
            if (key.equals("lr")) {
                int i = 1 / 0;
            }
            lock.setFlagSuccessMethod1();
            while (!lock.allSuccess()) {
                Thread.sleep(100);
            }
        } catch (Exception e) {
            log.error("insert 1 error", e);
            lock.setFlagErrorMethod1();
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void insert2(String key, TestThread lock) {
        // 创建随机数组成的数组:
        try {
            List<UserInfo> userInfos = new ArrayList<>(20);
            fillRandom(userInfos, 20, key);
            int loop = userInfos.size() / UserInfoSaveTask.THRESHOLD;
            lock.checkError();
            int result = 0;
            for (int i = 0; i < loop; i++) {
                int start = i * UserInfoSaveTask.THRESHOLD;
                int end = (i + 1) * UserInfoSaveTask.THRESHOLD;
                if (loop - 1 == i) {
                    end = userInfos.size();
                }
                int sum = userInfoMapper.insertList(userInfos.subList(start, end));
                System.out.println("insert2 single insert " + sum);
              //  Thread.sleep(5000);
                result = sum + result;
                lock.checkError();
            }
            if ("lr2".equals(key)) {
                int i = 1 / 0;
            }
            lock.setFlagSuccessMethod2();
            while (!lock.allSuccess()) {
                Thread.sleep(100);
            }
        } catch (Exception e) {
            log.error("method 2 exec error {}", e);
            lock.setFlagErrorMethod2();
        }

    }

    private UserInfo createOneUser(String key) {
        UserInfo userInfo = new UserInfo();
        userInfo.setUsername(key);
        userInfo.setPassword(key);
        userInfo.setEmail(key);
        userInfo.setUsertype("0");
        userInfo.setEnabled(0);
        userInfo.setQq(key);
        userInfo.setTel(key);
        return userInfo;
    }

    public TestThread getLock(String key) {
        System.out.println("================= 线程获取锁 : " + Thread.currentThread().getId() + " map size: " + map.size());
        if (map.get(key) != null) {
            throw new RuntimeException("线程没们获取到锁 : " + Thread.currentThread().getId() + " map size: " + map.size());
        } else {
            map.putIfAbsent(key, new TestThread());
            System.out.println(" success ================= 线程获取锁 : " + Thread.currentThread().getId() + " map size: " + map.size());
            return map.get(key);
        }
//        TestThread testThread =
//        if (testThread != null) {
//            throw new RuntimeException("线程没们获取到锁 : " + Thread.currentThread().getId() + " map size: " + map.size());
//        }

    }

    private void removeLock(String key, ReentrantLock lock) {
        if (map.get(key).equals(lock)) {
            map.remove(key);
        }
    }

}
